Spring AOP的实现已经臻于非常完善,而通过与AspectJ的整合使得AOP的使用简单且灵活。不论是XML还是注解,都实现了非侵入式的控制。而基于自动代理的基础上,整合的AspectJ也通过BeanPostProcessor扩展的方式实现细粒度的切面控制。XML方式通过以aop:config标签实现配置,注解方式则通过@Aspect声明切面类。两种方式底层的实现殊途同归,都是基于自动代理的基类AbstractAutoProxyCreator来完成。
Spring的组件通过XML配置进行注册以及初始化,其方式就是实现特定命名空间的NamespaceHandler接口,对于Spring+AspectJ的整合方式的XML配置,是从AopNamespaceHandler开始。其中注册了两个标签,config和aspectj-autoproxy,分别为XML配置的根标签,和注解方式的启用配置。
1 | registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); |
不同的解析器对应的处理最终实现了XML或注解方式的AspectJ AOP。
1.XML配置
先看一个XML配置的demo
1 |
|
ConfigBeanDefinitionParser解析器用来处理XML配置,Spring的代码大多使用命名清晰的子方法来描述主结构。
1 | ConfigBeanDefinitionParser.java |
parse方法的主要内容分为两部分,自动代理配置的创建以及代理XML配置的解析,可以从上面代码中很清晰的看出。
自动代理配置的创建
自动代理配置是基类AbstractAutoProxyCreator的子类AspectJAwareAdvisorAutoProxyCreator,来实现AspectJ相关的AOP的实现。其主要的类结构如下:
- AbstractAutoProxyCreator:基于BeanPostProcessor扩展完成AOP代理的创建
- AbstractAdvisorAutoProxyCreator:切面的发现和匹配
- AspectJAwareAdvisorAutoProxyCreator:AspectJ相关支持
而configureAutoProxyCreator方法则完成了自动代理配置的初始化。
1 | private void configureAutoProxyCreator(ParserContext parserContext, Element element) { |
由工具类AopNamespaceUtils实现
1 | AopNamespaceUtils.java |
对于AspectJ自动代理创建类的注册有一个优先级机制,即当前容器中已存在自动代理创建类的bean,则以优先级高的替换优先级低的。
1 | AopConfigUtils.java |
对于自动代理创建类的优先级,在Spring中定义了三个
1 | // 基础版 |
因而同时存在XML和注解时,注解的自动代理创建类会覆盖XML的。但AnnotationAwareAspectJAutoProxyCreator其实是AspectJAwareAdvisorAutoProxyCreator的子类,在查询候选Advisor时,会先调用父类的方法获取XML配置中的Advisor。
另外aop:config可以配置proxy-target-class和expose-proxy,通过useClassProxyingIfNecessary方法设置到AspectJAwareAdvisorAutoProxyCreator的BeanDefinition的属性中。
1 | AopNamespaceUtils.java |
代理XML配置的解析
注册完自动代理创建类,接下来就是aop的具体配置。常用的一般是aop:pointcut和aop:aspect两个标签,aop:advisor通常在外部aop:config外存在advice配置时使用。
对pointcut的解析比较简单,就是获取id及expression属性,然后创建pointcut的BeanDefinition。
1 | ConfigBeanDefinitionParser.java |
主要来看下对aspect的解析,在aop:aspect中的有两类子标签,一种是pointcut切入点的配置,一种是advice增强的配置,而advice又分为前置增强,后置增强,环绕增强等。
1 | ConfigBeanDefinitionParser.java |
对advice增强的解析parseAdvice方法是核心部分,而其返回的是组装好的Advisor切面BeanDefinition
1 | ConfigBeanDefinitionParser.java |
此方法中先创建了两个合成的BeanDefinition,一个为增强方法的工厂,一个为切面对象的工厂,用来最终通过反射调用时使用。而后根据不同的advice标签(aop:before,aop:after-returning等)创建相应的增强BeanDefinition,最后使用AspectJPointcutAdvisor封装增强BeanDefinition然后返回。
再来看看对advice标签的解析createAdviceDefinition方法
1 | private AbstractBeanDefinition createAdviceDefinition( |
不同的Advice标签对应不同的Advice类,但都继承同一个基类AbstractAspectJAdvice。AbstractAspectJAdvice定义了构造函数
1 | public AbstractAspectJAdvice( |
创建Advice的BeanDefinition时,即按照此构造函数组装BeanDefinition中的ConstructorArgumentValues属性。
而对于不同的advice则通过getAdviceClass方法匹配对应的Class
1 | private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) { |
至此,每个Advice都设置了切入点,切面类以及增强方法,再由AspectJPointcutAdvisor对Advice进行封装,在每个bean初始化之后,AspectJAwareAdvisorAutoProxyCreator的基类AbstractAutoProxyCreator实现了BeanPostProcessor扩展,查询所有的匹配bean的Advisor,并创建bean对应的Proxy代理,在方法真正执行时,触发其相应的Advice执行。
2.注解配置
不论是通过<aop:aspectj-autoproxy/>
还是@EnableAspectJAutoProxy
配置的AspectJ注解支持,都是通过AnnotationAwareAspectJAutoProxyCreator支撑对AspectJ相关注解的解析和注册。注册AnnotationAwareAspectJAutoProxyCreator的过程是通过AspectJAutoProxyRegistrar
实现的。
AnnotationAwareAspectJAutoProxyCreator继承AnnotationAwareAspectJAutoProxyCreator,并覆盖了查询所有候选Advisor的方法findCandidateAdvisors。基于此方法对@Aspect的切面类进行解析,并生成相应Advisor对象返回。
1 | AnnotationAwareAspectJAutoProxyCreator.java |
核心操作交由BeanFactoryAspectJAdvisorsBuilder类的buildAspectJAdvisors方法实现
1 | BeanFactoryAspectJAdvisorsBuilder.java |
核心方法是根据Aspect实例工厂获取所有Advisor对象
1 | ReflectiveAspectJAdvisorFactory.java |
getAdvisor方法执行具体的操作
1 | ReflectiveAspectJAdvisorFactory.java |
Advisor对应的Advice对象的实例化实际发生在InstantiationModelAwarePointcutAdvisorImpl的构造方法中
1 | public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, |
通过ReflectiveAspectJAdvisorFactory工厂类完成
1 | public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, |
至此,注解方式的切面类中的每个Advice方法生成对应的Advice对象,并被InstantiationModelAwarePointcutAdvisor实现类封装然后返回。
相比于XML配置的方式,注解方式的Advisor不会生成BeanDefinition注册到Spring容器中,而是直接返回到Advisor集合中,并以aspectName的方式进行缓存防止重复生成及性能优化。
对于Spring+AspectJ的方式,其主要操作都在于Advisor的解析和生产,底层通过Spring自动代理的方式被Spring容器初始化bean时调用。而AOP代理则是使用ProxyFactory,根据不同配置决定JDK或CGLIB的方式来生成。